package com.xm.qrcode.core;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 二维码生成器
 *  //取自：https://blog.csdn.net/peak_paradise/article/details/144560950
 *
 * @author gaof
 * @date 2024/12/18
 */
@Getter
@Setter
@Accessors(fluent = true)
public class QRCodeGenerator {
 
    /**
     * 默认的logo宽度 设置太大会遮挡二维码信息
     */
    private static final int DEFAULT_LOGO_WIDTH = 80;
 
    /**
     * 默认的logo高度 设置太大会遮挡二维码信息
     */
    private static final int DEFAULT_LOGO_HEIGHT = 80;
 
    /**
     * 默认的logo与二维码之间的间距
     */
    private static final int DEFAULT_PADDING = 10;
 
    /**
     * 默认的二维码宽度
     */
    private static final int DEFAULT_WIDTH = 350;
 
    /**
     * 默认的二维码高度
     */
    private static final int DEFAULT_HEIGHT = 350;
 
    /**
     * 默认的字符集
     */
    private static final String DEFAULT_CHARSET = "UTF-8";
 
    /**
     * 默认的图片格式
     */
    private static final String DEFAULT_IMAGE_FORMAT = "PNG";
 
    /**
     * 二维码宽度
     */
    private int width = DEFAULT_WIDTH;
 
    /**
     * 二维码高度
     */
    private int height = DEFAULT_HEIGHT;
 
    /**
     * logo的宽度
     */
    private int logoWidth = DEFAULT_LOGO_WIDTH;
 
    /**
     * logo的高度
     */
    private int logoHeight = DEFAULT_LOGO_HEIGHT;
 
    /**
     * logo画布的边距
     */
    private int logoPadding = DEFAULT_PADDING;
 
    /**
     * 图片格式
     */
    private String imageFormat = DEFAULT_IMAGE_FORMAT;
 
    /**
     * 二维码内容
     */
    private String content;
    
    /**
     * logo图片路径
     */
    private String logoPath;
 
    /**
     * logo图片流
     */
    private InputStream logoInputStream;
 
    /**
     * 字符集
     */
    private String charset = DEFAULT_CHARSET;
 
    /**
     * 构造器，不对外提供
     */
    private QRCodeGenerator() {
    }
 
    /**
     * 构建二维码生成器
     *
     * @param content 二维码内容
     * @return 二维码生成器
     */
    public static QRCodeGenerator of(String content) {
        return new QRCodeGenerator().content(content);
    }

//    private QRCodeGenerator content(String content) {
//        this.content = content;
//        return null;
//    }
//
//    private QRCodeGenerator logoInputStream(FileInputStream logoInputStream) {
//        this.logoInputStream = logoInputStream;
//        return null;
//    }
//
//    private QRCodeGenerator logoPath(String logoPath) {
//        this.logoPath = logoPath;
//        return null;
//    }

    /**
     * 生成二维码
     *
     * @return 二维码图片
     * @throws WriterException 编码异常
     * @throws IOException     IO异常
     */
    public BufferedImage toQRImage() throws WriterException, IOException {
        if (this.content == null || this.content.isEmpty()) {
            throw new RuntimeException("生成的内容不能为空");
        }
        // 生成二维码Writer
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        // 设置二维码参数
        Map<EncodeHintType, Object> hints = new HashMap<>();
        // 设置二维码纠错级别
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 设置二维码字符集
        hints.put(EncodeHintType.CHARACTER_SET, this.charset);
        // 设置二维码边距
        hints.put(EncodeHintType.MARGIN, 1);
        // 生成二维码
        BitMatrix bitMatrix = multiFormatWriter.encode(this.content, BarcodeFormat.QR_CODE, this.width, this.height, hints);
        // 配置二维码色彩，不然LOGO就是黑白了
        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
        BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
        // 读取logo图片,进行缩放，并加入二维码中心处
        if (this.logoPath != null && !this.logoPath.isEmpty()) {
            insertLogo(image, compressLogo(readLogoFromFile(logoPath)));
        } else if (this.logoInputStream != null) {
            insertLogo(image, compressLogo(ImageIO.read(this.logoInputStream)));
        }
        return image;
    }
 
    /**
     * 生成二维码并保存到文件
     *
     * @param filePath 文件路径
     * @throws WriterException 编码异常
     * @throws IOException     IO异常
     */
    public void toQRFile(String filePath) throws WriterException, IOException {
        BufferedImage image = toQRImage();
        // 保存二维码
        ImageIO.write(image, this.imageFormat, new File(filePath));
    }
 
    /**
     * 插入logo
     *
     * @param source 原图
     * @param logo   logo图片
     * @throws IOException IO异常
     */
    private void insertLogo(BufferedImage source, BufferedImage logo) throws IOException {
        int w = this.logoWidth + 2 * this.logoPadding;
        int h = this.logoHeight + 2 * this.logoPadding;
        // 创建一个白色边框的画布，把LOGO放在中间
        BufferedImage whiteBorderLogo = drawLogoWhiteBorder(w, h, logo);
 
        int x = (this.width - w) / 2;// 85
        int y = (this.height - h) / 2;// 85
        // 启动画LOGO的画布
        Graphics2D graph = source.createGraphics();
        // 绘制LOGO
        graph.drawImage(whiteBorderLogo, x, y, w, h, null);
        // 完成
        graph.dispose();
    }
 
    /**
     * 读取logo图片，并进行缩放
     *
     * @param logoPath logo图片路径
     * @return logo图片
     * @throws IOException IO异常
     */
    private static BufferedImage readLogoFromFile(String logoPath) throws IOException {
        File file = new File(logoPath);
        if (!file.exists()) {
            throw new RuntimeException("LOGO图片未找到");
        }
        return ImageIO.read(new File(logoPath));
    }
 
    /**
     * 压缩logo
     *
     * @param logo logo图片
     * @return 压缩后的logo图片
     * @throws IOException IO异常
     */
    private BufferedImage compressLogo(BufferedImage logo) throws IOException {
        return Thumbnails.of(logo).size(this.logoWidth, this.logoHeight).asBufferedImage();
    }
 
    /**
     * 绘制logo白边
     *
     * @param w    logo宽度
     * @param h    logo高度
     * @param logo logo图片
     * @return logo图片
     */
    private BufferedImage drawLogoWhiteBorder(int w, int h, BufferedImage logo) {
        BufferedImage whiteBorderLogo = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D logoGraph = whiteBorderLogo.createGraphics();
        logoGraph.setColor(Color.WHITE);
        logoGraph.fillRect(0, 0, w, h);
        logoGraph.drawImage(logo, this.logoPadding, this.logoPadding, null);
        logoGraph.dispose();
        return whiteBorderLogo;
    }
 
    public static void main(String[] args) throws IOException, WriterException {
//        QRCodeGenerator
////                .of("https://www.baidu.com")
//                .of("https://www.cnblogs.com/huakaiyoushi/p/13992723.html")
//                .logoPath("")
//                .toQRFile("D:\\javaitems\\二维码测试\\qrcode_logo4.png");
//
//        QRCodeGenerator
////                .of("https://www.baidu.com")
//                .of("https://www.cnblogs.com/huakaiyoushi/p/13992723.html")
//                .logoPath("D:\\javaitems\\二维码测试\\善逸_1080.jpg")
//                .toQRFile("D:\\javaitems\\二维码测试\\qrcode_logo3.png");

        String content = Arrays.toString("你好 世界".getBytes("utf-8"));
//        content = "https://www.baidu.com";
        content = "https://www.cnblogs.com/huakaiyoushi/p/10408431.html";

        // 花生壳域名 虚拟机：192.168.119.128
        content = "http://867xq382oj37.vicp.fun/";
        content = "http://867xq382oj37.vicp.fun:50757";
//        content = "123";
        BufferedImage qrImage = QRCodeGenerator
                .of(content)
//                .logoInputStream(new FileInputStream("D:\\javaitems\\二维码测试\\善逸_1080.jpg"))
//                .logoInputStream(new FileInputStream("D:\\javaitems\\二维码测试\\企鹅linux.png"))
//                .logoInputStream(new FileInputStream("D:\\javaitems\\二维码测试\\最新通知.png"))
                .logoInputStream(new FileInputStream("D:\\javaitems\\二维码测试\\哈士奇.png"))
                .toQRImage();
//        ImageIO.write(qrImage, "PNG", new File("D:\\javaitems\\二维码测试\\qrcode_logo_最新通知.png"));
        ImageIO.write(qrImage, "PNG", new File("D:\\javaitems\\二维码测试\\qrcode_logo_动态域名.png"));
    }
}

